package scoutobahn.highway;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;

import scoutobahn.util.Tuple;
import scoutobahn.xml.OsmRelation;

/**
 * A node that represents a highway.
 * @author mark and florian koch
 *
 */
public class Highway extends HighwayElement {
    private HighwayNode[] nodes;
    private final OsmRelation relation;
    private final DistanceCalculator calculator;
    /**
     * Constructs a new Highway.
     * @param osmRelation The OsmRelation of the Highway
     * @param newCalculator The DistanceCalculator to use
     */
    public Highway(final OsmRelation osmRelation,
            final DistanceCalculator newCalculator) {
        super(osmRelation, new LinkedList<HighwayElement>());
        this.relation = osmRelation;
        this.calculator = newCalculator;
    }
    /**
     * Sets the OsmNode that form this Highway.
     * @param osmNodes The OsmNode that form this Highway
     */
    public void setNodes(final Collection<HighwayNode> osmNodes) {
        this.nodes = new HighwayNode[osmNodes.size()];
        int i = 0;
        for (HighwayNode node : osmNodes) {
            this.nodes[i] = node;
            i++;
        }
    }
    /**
     * Returns the next Intersections (HighwayNodes with more than one parent)
     * from the given HighwayNode.
     * @param from The HighwayNode to look from.
     * @return Two HighwayNode that are the nearest intersections
     */
    public Tuple<HighwayNode, HighwayNode> getNextIntersections(
            final HighwayNode from) {
        HighwayNode lastIntersectionNode = null;
        HighwayNode nextIntersectionNode = null;
        boolean found = false;
        for (int i = 0; i < this.nodes.length; i++) {
            if (this.nodes[i].getParents().size() > 1 && found) {
                nextIntersectionNode = this.nodes[i];
                return new Tuple<HighwayNode, HighwayNode>(lastIntersectionNode,
                        nextIntersectionNode);
            }
            if (this.nodes[i].equals(from)) {
                found = true;
            }
            if (this.nodes[i].getParents().size() > 1 && !found) {
                lastIntersectionNode = this.nodes[i];
            }
        }
        return new Tuple<HighwayNode, HighwayNode>(lastIntersectionNode,
                nextIntersectionNode);
    }
    /**
     * Returns the distance between the two given HighwayNodes on this Highway.
     * @param node1 One OsmNode
     * @param node2 Other OsmNode
     * @return The distance on this Highway
     */
    public double getDistance(final HighwayNode node1,
            final HighwayNode node2) {
        Collection<HighwayNode> nodesInBetween =
                this.getNodesBetween(node1, node2);
        double dist = 0;
        HighwayNode lastNode = null;
        if (nodesInBetween.size() <= 1) {
            return this.calculator.getDistance(node1.getOsmItem(),
                    node2.getOsmItem());
        }
        for (HighwayNode node : nodesInBetween) {
            if (lastNode != null) {
                dist = dist + this.calculator.getDistance(lastNode.getOsmItem(),
                        node.getOsmItem());
            }
            lastNode = node;
        }
        return dist;
    }
    private Collection<HighwayNode> getNodesBetween(final HighwayNode node1,
            final HighwayNode node2) {
        boolean found = false;
        LinkedList<HighwayNode> nodesBetween = new LinkedList<HighwayNode>();
        for (int i = 0; i < this.nodes.length; i++) {
            if (found) {
                nodesBetween.add(this.nodes[i]);
            }
            if (found && this.nodes[i].equals(node1)
                    || this.nodes[i].equals(node2)) {
                return nodesBetween;
            }
            if (this.nodes[i].equals(node1) || this.nodes[i].equals(node2)) {
                found = true;
            }
        }
        if (!found) {
            throw new IllegalArgumentException("Nodes not found!");
        }
        return nodesBetween;
    }
    /**
     * Returns a collection of all HighwayNodes that form this Highway.
     * @return A collection of HighwayNode
     */
    public Collection<HighwayNode> getHighwayNodes() {
        return Arrays.asList(this.nodes);
    }
    @Override
    public OsmRelation getOsmItem() {
        return this.relation;
    }
}
